package com.tiny.codegen.template.batis;

import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.sql.Connection;
import java.sql.DatabaseMetaData;
import java.sql.DriverManager;
import java.sql.ResultSet;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Properties;
import java.util.Set;

import org.apache.commons.lang3.StringUtils;

import com.tiny.codegen.common.Column;
import com.tiny.codegen.common.ColumnToFieldMapper;
import com.tiny.codegen.template.ITemplateGenerate;
import com.tiny.codegen.util.FreeMarkerTemplateUtil;
import com.tiny.codegen.util.StringUtil;

/**
 * myBatis生成工具
 * 
 * @author 刘涛
 * @date 2018年7月13日
 */
public class BatisGenerate implements ITemplateGenerate {

	/**
	 * 模板配置
	 */
	private BatisTemplateConfig config;
	/** 模板目录 **/
	private String templateDir;

	/**
	 * 本地测试使用
	 */
	public BatisGenerate() {
		this.templateDir = System.getProperty("user.dir") + "/src/main/resource/mybatis";
	}

	public BatisGenerate(String dir) {
		this.templateDir = System.getProperty("user.dir") +"/" + dir;
	}

	public static void main(String[] args) {
		try {
			 BatisGenerate batis = new BatisGenerate();
			 batis.generate();
		} catch (Exception e) {
			e.printStackTrace();
		}
	}

	@Override
	public void generate() throws Exception {
		// 读取配置
		loadConfig();
		if (config.getOutput() == null) {
			throw new NullPointerException("生成目录不能为空");
		}

		Connection connection = null;
		try {
			// 获取数据连接
			connection = getConnection();
			config.setDatabase(connection.getCatalog());
			DatabaseMetaData metaData = connection.getMetaData();
			// 获取表注释
			List<Table> tableList = this.getTables(metaData);
			if (tableList == null || tableList.isEmpty()) {
				throw new NullPointerException("未找到相关表");
			}
			for (Table table : tableList) {
				this.generateCode(table);
			}
		} catch (Exception e) {
			throw e;
		} finally {
			if (null != connection) {
				connection.close();
			}
		}
	}
	
	/**
	 * 加载配置信息
	 */
	private void loadConfig() {
		Properties prop = new Properties();
		try {
			prop.load(new FileInputStream(new File(templateDir, "config.properties")));
		} catch (IOException e) {
			throw new NullPointerException("未找到配置文件config.properties");
		}
		config = new BatisTemplateConfig();
		config.setAuthor(prop.getProperty("template.author", prop.getProperty("template.author")));
		config.setOutput(prop.getProperty("template.outpath"));
		config.setDatabase(prop.getProperty("template.database"));
		config.setTables(new HashSet<String>(Arrays.asList(prop.getProperty("template.tables", "").split(","))));
		config.setColumn_camel("true".equalsIgnoreCase(prop.getProperty("template.column.camel")));
		config.setTable_ignore_prefix(prop.getProperty("template.tables.ingore.preifx", ""));
		config.setPackage_mapper(prop.getProperty("template.package.mapper"));
		config.setPackage_entity(prop.getProperty("template.package.entity"));
		config.setPackage_dao(prop.getProperty("template.package.dao"));
		config.setPackage_service(prop.getProperty("template.package.service"));
		config.setPackage_serviceImpl(prop.getProperty("template.package.serviceImpl"));
		config.setPackage_controller(prop.getProperty("template.package.controller"));
		config.setSuffix_mapper(prop.getProperty("template.suffix.mapper", "Mapper"));
		config.setSuffix_entity(prop.getProperty("template.suffix.entity", "Entity"));
		config.setSuffix_dao(prop.getProperty("template.suffix.dao", "Dao"));
		config.setSuffix_service(prop.getProperty("template.suffix.service", "Service"));
		config.setSuffix_serviceImpl(prop.getProperty("template.suffix.serviceImpl", "ServiceImpl"));
		config.setSuffix_controller(prop.getProperty("template.suffix.controller", "Controller"));
		
	}

	/**
	 * 生成代码
	 * 
	 * @param table
	 * @throws Excetion
	 */
	private void generateCode(Table table) throws Exception {
		FreeMarkerTemplateUtil templateUtil = new FreeMarkerTemplateUtil(templateDir);
		Map<String, Object> dataMap = new HashMap<String, Object>();
		dataMap.put("config", config);
		dataMap.put("table", table);
		dataMap.put("columns", table.getColumns());
		/*** 生成code  ***/
		//代码路径
		String codePath = config.getOutput() + "src/main/java/";
		
		String mapperPath = config.getOutput() + config.getPackage_mapper();
		//如果mapper在不在resources目录下,则修改包名为路径
		if(!mapperPath.contains("/resources/")) {
			mapperPath = codePath + StringUtil.replace(config.getPackage_mapper(), ".", File.separator);
		}
		// mappper文件
		templateUtil.generateFileByTemplate("mapper.xml", mapperPath,
				table.getClass_name() + config.getSuffix_mapper() + ".xml", dataMap);
		//java文件
		templateUtil.generateFileByTemplate("dao.ftl", codePath + StringUtil.replace(config.getPackage_dao(), ".", File.separator),
				table.getClass_name() + config.getSuffix_dao() + ".java", dataMap);
		templateUtil.generateFileByTemplate("entity.ftl", codePath + StringUtil.replace(config.getPackage_entity(), ".", File.separator), 
				table.getClass_name() + config.getSuffix_entity() + ".java", dataMap);
		templateUtil.generateFileByTemplate("service.ftl", codePath + StringUtil.replace(config.getPackage_service(), ".", File.separator), 
				table.getClass_name() + config.getSuffix_service() + ".java", dataMap);
		templateUtil.generateFileByTemplate("serviceImpl.ftl", codePath + StringUtil.replace(config.getPackage_serviceImpl(), ".", File.separator),
				table.getClass_name() + config.getSuffix_serviceImpl() + ".java", dataMap);
		templateUtil.generateFileByTemplate("controller.ftl", codePath + StringUtil.replace(config.getPackage_controller(), ".", File.separator),
				table.getClass_name() + config.getSuffix_controller() + ".java", dataMap);
		
	}
	
	/**
	 * 获取连接
	 * 
	 * @return
	 * @throws Exception
	 */
	private Connection getConnection() throws Exception {
		Properties prop = new Properties();
		try {
			prop.load(new FileInputStream(new File(templateDir, "jdbc.properties")));
		} catch (IOException e) {
			throw new NullPointerException("未找到配置文件jdbc.properties");
		}
		prop.setProperty("remarks", "true"); //设置可以获取remarks信息 
		prop.setProperty("useInformationSchema", "true"); //设置可以获取tables remarks信息
		Connection connection = DriverManager.getConnection(prop.getProperty("url"), prop);
		return connection;
	}

	/**
	 * 获取表对象
	 * 
	 * @param metaData
	 * @return
	 */
	private List<Table> getTables(DatabaseMetaData metaData) throws Exception {
		List<Table> tables = new ArrayList<Table>();
		
		Set<String> tableNames = config.getTables();
		for (String tableName : tableNames) {
			ResultSet rs = metaData.getTables(config.getDatabase(), null, tableName, new String[] { "TABLE" });
			while (rs.next()) {
				Table table = new Table();
				table.setAnnotation(rs.getString("REMARKS"));
				table.setName(rs.getString("TABLE_NAME"));
				table.setClass_name(StringUtil.toClass(table.getName().replace(config.getTable_ignore_prefix(), "")));

				ResultSet pkRs = metaData.getPrimaryKeys(config.getDatabase(), null, table.getName());
				while (pkRs.next()) {
					table.setPrimary_key(pkRs.getString("COLUMN_NAME"));
					table.setPrimary_key_field(StringUtil.toClassField(table.getPrimary_key()));
				}
				table.setColumns(this.getColumns(metaData, table.getName()));
				tables.add(table);
			}
		}
		return tables;
	}

	/**
	 * 获取字段对象
	 * 
	 * @param databaseMetaData
	 * @param tableName
	 * @return
	 * @throws Exception
	 */
	private List<Column> getColumns(DatabaseMetaData databaseMetaData, String tableName) throws Exception {
		List<Column> columns = new ArrayList<Column>();
		ResultSet rs = databaseMetaData.getColumns(config.getDatabase(), "%", tableName, "%");
		while (rs.next()) {
			Column column = new Column();
			// 获取字段名称
			column.setName(rs.getString("COLUMN_NAME"));
			// 获取字段类型
			column.setType(rs.getString("TYPE_NAME"));
			// 字段在数据库的注释
			column.setComment(rs.getString("REMARKS"));
			column.setField_name(config.getColumn_camel() ? StringUtil.toClassField(column.getName()) : column.getName());
			//解决无符号数据类型问题
			if(column.getType().contains(" UNSIGNED")) {
				column.setType(column.getType().replaceAll(" UNSIGNED", ""));
			}
			//mybatis 没有INT,TEXT类型
			if(column.getType().equals("INT")) {
				column.setType("INTEGER");
			}
			if(column.getType().equals("TEXT")) {
				column.setType("VARCHAR");
			}
			
			column.setField_type(ColumnToFieldMapper.mapperMap.get(column.getType()));
			// 如果数据库字段没有对应的映射类,则抛出异常
			if (StringUtils.isEmpty(column.getField_type())) {
				throw new NullPointerException("字段 " + column.getName() +",数据库类型："+ column.getType() + ",没有找到对应的类型");
			}
			columns.add(column);
		}
		return columns;
	}

}
